using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
namespace Foundry.Graphics
{
    public sealed class VertexArray<T> : IHandle, IDisposable
        where T : struct
    {
        public int Handle { get; private set; }
        public bool Disposed { get; private set; }
        public event EventHandler Disposing;
        public VertexDescriptor Descriptor { get; private set; }
		
		
		/// <summary>
		/// Gets the supplied vertex buffer 
		/// </summary>
        public VertexBuffer<T> Buffer { get; private set; }

        public void MakeCurrent()
        {
            if (Disposed)
                throw new ObjectDisposedException(GetType().FullName);
			
            GL.BindVertexArray(Handle);
			Buffer.MakeCurrent();
			
			//Build(Descriptor);
			Extensions.AssertGL();
        }

        public void MakeNullCurrent()
        {
            //GL.BindVertexArray(0);
        }
		
		/// <summary>
		/// Builds a vertexarray using the supplied descriptor 
		/// </summary>
		/// <param name="desc">
		/// A <see cref="VertexDescriptor"/> determining the layout of the structure
		/// </param>
        public VertexArray(VertexDescriptor desc)
        {
            Buffer = new VertexBuffer<T>();
            Build(desc);
        }

		
		/// <summary>
		/// Builds a vertex array using default behaviour 
		/// </summary>
        public VertexArray()
        {
            Buffer = new VertexBuffer<T>();
            var t = typeof(T);

            var descriptor = t.GetCustomAttributes(typeof(DescriptorAttribute), false);
            if (descriptor != null && descriptor.Length > 0)
            {
                var d = descriptor[0] as DescriptorAttribute;
                var field = t.GetField(d.DescriptorFieldName);
                if (!field.IsStatic)
                    throw new NotSupportedException("Descriptor field must be static.");

				var desc = field.GetValue(null) as VertexDescriptor;
                Build(desc);
               
            }
        }

        private void Build(VertexDescriptor desc)
        {

            int new_handle = 0;
            GL.GenVertexArrays(1, out new_handle);
            Handle = new_handle;
            Descriptor = desc;
            GL.BindVertexArray(Handle);
            Buffer.MakeCurrent();
            for(int i = 0; i < desc.Items.Count; i++)
            {
                if(desc.Items[i].Type != DataType.Ignore)
                {
                    GL.EnableVertexAttribArray(i);
                    if((desc.Items[i].Type & (DataType)0x01) == (DataType)0x01)
                    {
                        VertexAttribIPointerType tp = VertexAttribIPointerType.UnsignedInt;
                        switch(desc.Items[i].Type)
                        {
                            case DataType.Byte:
                                tp = VertexAttribIPointerType.Byte;
                                break;
                            case DataType.UByte:
                                tp = VertexAttribIPointerType.UnsignedByte;
                                break;
                            case DataType.Short:
                                tp = VertexAttribIPointerType.Short;
                                break;
                            case DataType.UShort:
                                tp = VertexAttribIPointerType.UnsignedShort;
                                break;
                            case DataType.Int:
                                tp = VertexAttribIPointerType.Int;
                                break;
                            case DataType.UInt:
                                tp = VertexAttribIPointerType.UnsignedInt;
                                break;
                        }

                        GL.VertexAttribIPointer(i, desc.Items[i].Elements, tp, desc.TotalSize, new IntPtr(desc.Items[i].Offset));
                        
                       
                    }
                    else
                    {
                        VertexAttribPointerType tp = VertexAttribPointerType.Float;
                        switch(desc.Items[i].Type)
                        {
                            case DataType.Half:
                                tp = VertexAttribPointerType.HalfFloat;
                                break;
                            case DataType.Float:
                                tp = VertexAttribPointerType.Float;
                                break;
                            case DataType.Double:
                                tp = VertexAttribPointerType.Double;
                                break;
                        }
                        
                        //GL.VertexAttribPointer(i, desc.Items[i].Elements, tp, false, desc.TotalSize, new IntPtr(desc.Items[i].Offset));
                        var it = desc.Items[i];
                        GL.VertexAttribPointer(i, it.Elements, tp,false, desc.TotalSize, new IntPtr(it.Offset));
                    }
					
                }
            }
            //MakeNullCurrent();
        }
        public void Dispose()
        {
            if(OpenTK.Graphics.GraphicsContext.CurrentContext != null && !Disposed)
			{
	            GC.SuppressFinalize(this);
	            if (Disposing != null)
	                Disposing(this, EventArgs.Empty);
	            GL.DeleteVertexArrays(1, new int[] { Handle });
	            Buffer.Dispose();
	            Handle = 0;
	            Disposed = true;
			}
        }
		
		~VertexArray()
		{
			var va_handle = Handle;
			if(Foundry.Engine.App.CurrentApplication != null)
			{
				Foundry.Engine.App.CurrentApplication.DispatchResourceCallStack(()=> {GL.DeleteVertexArrays(1, new int[] {va_handle});} );
			}
		}
    }
}

